/**
 * Copyright (C) 2011 The Android Open Source Project 
 * Emial : guoyil199@163.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package com.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.UUID;

import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.telephony.TelephonyManager;
import android.util.Log;

/**
 * @author Administrator
 * @Email guoyil199@163.com
 * @version V1.0
 * @Date 2015年11月26日 上午10:19:51
 * @Title <B>todo</B>
 * 
 *        <pre>
 * todo
 * </pre>
 */

public class DroidUnique {

	// (1).DEVICE_ID: 权限：READ_PHONE_STATE， 非Android手机设备，可能获取不到。
	public static String getDeviceID(Context context) {
		// 同时可以获取：IMEI，MEID或者ESN码
		TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
		String DEVICE_ID = tm.getDeviceId();
		return DEVICE_ID;
	}

	// MAC ADDRESS 获取手机Wifi或蓝牙的MAC地址作为设备标识 权限：ACCESS_WIFI_STATE
	// 硬件限制：并不是所有的设备都有Wifi和蓝牙硬件，硬件不存在自然也就得不到这一信息。
	// 获取的限制：如果Wifi没有打开过，是无法获取其Mac地址的；而蓝牙是只有在打开的时候才能获取到其Mac地址
	// Wifi Mac：
	public static String getWifiMac(Context context) {
		// -- wifi 必须打开
		WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
		WifiInfo info = wifi.getConnectionInfo();
		String wifiMac = info.getMacAddress();
		return wifiMac;
	}

	public static String getWifiMac2(Context context) {
		// -- 使用命令
		String macSerial = null;
		String str = "";
		try {
			//文件路径 /sys/class/net/wlan0/address
			//adb shell  cat /sys/class/net/wlan0/address     
			Process pp = Runtime.getRuntime().exec("cat /sys/class/net/wlan0/address");
			InputStreamReader ir = new InputStreamReader(pp.getInputStream());
			LineNumberReader input = new LineNumberReader(ir);
			for (; null != str;) {
				str = input.readLine();
				if (str != null) {
					macSerial = str.trim();// 去空格
					break;
				}
			}
		} catch (IOException ex) {
			// 赋予默认值
			ex.printStackTrace();
		}
		return macSerial;
	}

	/*
	 * ****************************************************************
	 * 子函数：获得本地MAC地址
	 * ****************************************************************
	 */
	public static String getMacAddress() {
		String result = "";
		String Mac = "";
		result = callCmd("busybox ifconfig", "HWaddr");

		// 如果返回的result == null，则说明网络不可取
		if (result == null) {
			return "网络出错，请检查网络";
		}
		// 对该行数据进行解析
		// 例如：eth0 Link encap:Ethernet HWaddr 00:16:E8:3E:DF:67
		if (result.length() > 0 && result.contains("HWaddr") == true) {
			Mac = result.substring(result.indexOf("HWaddr") + 6, result.length() - 1);
			Log.i("test", "Mac:" + Mac + " Mac.length: " + Mac.length());

			if (Mac.length() > 1) {
				Mac = Mac.replaceAll(" ", "");
				result = "";
				String[] tmp = Mac.split(":");
				for (int i = 0; i < tmp.length; ++i) {
					result += tmp[i];
				}
			}
			Log.i("test", result + " result.length: " + result.length());
		}
		return result;
	}

	public static String callCmd(String cmd, String filter) {
		String result = "";
		String line = "";
		try {
			Process proc = Runtime.getRuntime().exec(cmd);
			InputStreamReader is = new InputStreamReader(proc.getInputStream());
			BufferedReader br = new BufferedReader(is);
			// 执行命令cmd，只取结果中含有filter的这一行
			while ((line = br.readLine()) != null && line.contains(filter) == false) {
				// result += line;
				Log.i("test", "line: " + line);
			}
			result = line;
			Log.i("test", "result: " + result);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	// 获取蓝牙 Mac地址
	public static String getBluetoothMac(Context ctx) {
		String btMac = "";
		BluetoothAdapter bAdapt = BluetoothAdapter.getDefaultAdapter();
		if (bAdapt != null) {
			btMac = bAdapt.getAddress();
		} else {
			btMac = "No Bluetooth Device!";
		}
		return btMac;
	}

	// Sim Serial Number
	public static String getSimSerialNumber(Context ctx) {
		// 装有SIM卡的设备，可以通过下面的方法获取到Sim Serial Number
		TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
		String simSerialNumber = tm.getSimSerialNumber();
		return simSerialNumber;
	}

	// ANDROID_ID
	// 在设备首次启动时，系统会随机生成一个64位的数字，并把这个数字以16进制字符串的形式保存下来，这个16进制的字符串就是ANDROID_ID，当设备被wipe后该值会被重置。可以通过下面的方法获取：
	//
	public static String getAndroidID(Context ctx) {
		/*
		 * ANDROID_ID可以作为设备标识，但需要注意：
		 * 厂商定制系统的Bug：不同的设备可能会产生相同的ANDROID_ID：9774d56d682e549c。
		 * 厂商定制系统的Bug：有些设备返回的值为null。
		 * 设备差异：对于CDMA设备，ANDROID_ID和TelephonyManager.getDeviceId() 返回相同的值。
		 */
		String android_id = android.provider.Settings.Secure.getString(ctx.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
		//String android_id = android.provider.Settings.System.getString(ctx.getContentResolver(), android.provider.Settings.System.ANDROID_ID);
		return android_id;
	}

	// Android系统2.3版本以上可以通过下面的方法得到Serial Number，且非手机设备也可以通过该接口获取。
	@TargetApi(Build.VERSION_CODES.GINGERBREAD)
	public static String getSerialNumber(Context ctx) {
		// Android系统2.3版本以上可以通过下面的方法得到Serial Number，且非手机设备也可以通过该接口获取。
		String serialNumber = android.os.Build.SERIAL;
		return serialNumber;
	}
	
	/*public static String getIMSI(){
		String imsi = android.os.SystemProperties.get(android.telephony.TelephonyProperties.PROPERTY_IMSI);
		//// within my emulator it returns: 310995000000000 
		return imsi;
	}*/
	
	/*public static String getIMEI(){
		// within my emulator it returns: 000000000000000 
		String imei = android.os.SystemProperties.get(android.telephony.TelephonyProperties.PROPERTY_IMEI); 
		return imei;
	}*/
	
	

	
	

	//Installtion ID UUID 设备类型无关
	public static String getInstalltionID(Context ctx) {
		// 原理是在程序安装后第一次运行时生成一个ID，该方式和设备唯一标识不一样，不同的应用程序会产生不同的ID，同一个程序重新安装也会不同。
		//所以这不是设备的唯一ID，但是可以保证每个用户的ID是不同的。可以说是用来标识每一份应用程序的唯一ID（即Installtion
		// ID），可以用来跟踪应用的安装数量
		return Installation.id(ctx);
	}
	
	//Google Developer Blog
	static class Installation {
		private static String sID = null;
		private static final String INSTALLATION = "INSTALLATION";

		public synchronized static String id(Context context) {
			if (sID == null) {
				File installation = new File(context.getFilesDir(), INSTALLATION);
				try {
					if (!installation.exists())
						writeInstallationFile(installation);
					sID = readInstallationFile(installation);
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
			return sID;
		}

		private static String readInstallationFile(File installation) throws IOException {
			RandomAccessFile f = new RandomAccessFile(installation, "r");
			byte[] bytes = new byte[(int) f.length()];
			f.readFully(bytes);
			f.close();
			return new String(bytes);
		}

		private static void writeInstallationFile(File installation) throws IOException {
			FileOutputStream out = new FileOutputStream(installation);
			String id = UUID.randomUUID().toString();
			out.write(id.getBytes());
			out.close();
		}
	}
	
	static class DeviceUuidFactory{
		 	protected static final String PREFS_FILE = "device_id.xml";
		    protected static final String PREFS_DEVICE_ID = "device_id";
		    protected static UUID uuid;
		    public DeviceUuidFactory(Context context) {
		        if( uuid ==null ) {
		            synchronized (DeviceUuidFactory.class) {
		                if( uuid == null) {
		                    final SharedPreferences prefs = context.getSharedPreferences( PREFS_FILE, 0);
		                    final String id = prefs.getString(PREFS_DEVICE_ID, null );
		                    if (id != null) {
		                        // Use the ids previously computed and stored in the prefs file
		                        uuid = UUID.fromString(id);
		                    } else {
		                        final String androidId = android.provider.Settings.Secure.getString(context.getContentResolver(),android.provider.Settings.Secure.ANDROID_ID);

		                        // Use the Android ID unless it's broken, in which case fallback on deviceId,
		                        // unless it's not available, then fallback on a random number which we store
		                        // to a prefs file
		                        try {
		                            if (!"9774d56d682e549c".equals(androidId)) {
		                                uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf8"));
		                            } else {
		                                final String deviceId = ((TelephonyManager) context.getSystemService( Context.TELEPHONY_SERVICE )).getDeviceId();
		                                uuid = deviceId!=null ? UUID.nameUUIDFromBytes(deviceId.getBytes("utf8")) : UUID.randomUUID();
		                            }
		                        } catch (UnsupportedEncodingException e) {
		                            throw new RuntimeException(e);
		                        }

		                        // Write the value out to the prefs file
		                        prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString() ).commit();

		                    }

		                }
		            }
		        }

		    }


		    /**
		     * Returns a unique UUID for the current android device.  As with all UUIDs, this unique ID is "very highly likely"
		     * to be unique across all Android devices.  Much more so than ANDROID_ID is.
		     *
		     * The UUID is generated by using ANDROID_ID as the base key if appropriate, falling back on
		     * TelephonyManager.getDeviceID() if ANDROID_ID is known to be incorrect, and finally falling back
		     * on a random UUID that's persisted to SharedPreferences if getDeviceID() does not return a
		     * usable value.
		     *
		     * In some rare circumstances, this ID may change.  In particular, if the device is factory reset a new device ID
		     * may be generated.  In addition, if a user upgrades their phone from certain buggy implementations of Android 2.2
		     * to a newer, non-buggy version of Android, the device ID may change.  Or, if a user uninstalls your app on
		     * a device that has neither a proper Android ID nor a Device ID, this ID may change on reinstallation.
		     *
		     * Note that if the code falls back on using TelephonyManager.getDeviceId(), the resulting ID will NOT
		     * change after a factory reset.  Something to be aware of.
		     *
		     * Works around a bug in Android 2.2 for many devices when using ANDROID_ID directly.
		     *
		     * @see http://code.google.com/p/android/issues/detail?id=10603
		     *
		     * @return a UUID that may be used to uniquely identify your device for most purposes.
		     */
		    public UUID getDeviceUuid() {
		        return uuid;
		    }
	}

}
